feat(canvas): add code-pane nav section to channels pane#2688
Conversation
Extract the sidebar navigation section (New task, Home, Search, Inbox, Agents, Skills, MCP servers, Command Center) out of SidebarMenu into a self-contained SidebarNavSection component, and render it in both the Code pane and the Channels pane. The new component wires every item's active state, badge count, and click handler internally so it works in either layout. Items route to the existing code-space views; from the Channels pane that switches the layout back to Code, while Search opens the command menu in place. Generated-By: PostHog Code Task-Id: 93824635-f0b3-4b9d-bf15-f31a4e505939
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/ui/src/features/sidebar/components/SidebarNavSection.tsx:68-75
**Redundant `useTasks` subscription when composed inside `SidebarMenu`**
`SidebarNavSection` fetches `allTasks` via `useTasks` solely to compute `commandCenterActiveCount`. When this component renders inside `SidebarMenu`, `SidebarMenu` already calls `useTasks` with the identical arguments (`{ showAllUsers, showInternal }`). React Query deduplicates the network request, but there are now two live subscriptions to the same query. The self-containment goal is valid for the Channels pane, but it introduces a redundant hook invocation every time the component is used in the Code pane context.
Reviews (1): Last reviewed commit: "feat(canvas): add code-pane nav section ..." | Re-trigger Greptile |
| const showInternal = useSidebarStore((s) => s.showInternal); | ||
| const { data: allTasks = [] } = useTasks({ showAllUsers, showInternal }); | ||
| const taskIds = new Set(allTasks.map((t) => t.id)); | ||
| const commandCenterCells = useCommandCenterStore((s) => s.cells); | ||
| const commandCenterActiveCount = commandCenterCells.filter( | ||
| (taskId) => taskId != null && taskIds.has(taskId), | ||
| ).length; | ||
|
|
There was a problem hiding this comment.
Redundant
useTasks subscription when composed inside SidebarMenu
SidebarNavSection fetches allTasks via useTasks solely to compute commandCenterActiveCount. When this component renders inside SidebarMenu, SidebarMenu already calls useTasks with the identical arguments ({ showAllUsers, showInternal }). React Query deduplicates the network request, but there are now two live subscriptions to the same query. The self-containment goal is valid for the Channels pane, but it introduces a redundant hook invocation every time the component is used in the Code pane context.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/sidebar/components/SidebarNavSection.tsx
Line: 68-75
Comment:
**Redundant `useTasks` subscription when composed inside `SidebarMenu`**
`SidebarNavSection` fetches `allTasks` via `useTasks` solely to compute `commandCenterActiveCount`. When this component renders inside `SidebarMenu`, `SidebarMenu` already calls `useTasks` with the identical arguments (`{ showAllUsers, showInternal }`). React Query deduplicates the network request, but there are now two live subscriptions to the same query. The self-containment goal is valid for the Channels pane, but it introduces a redundant hook invocation every time the component is used in the Code pane context.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Addressed in 7363ab0. SidebarMenu now derives the command-center count from the task map and cells it already holds, and passes it down; SidebarNavSection gates its own useTasks with enabled so it only subscribes when rendered standalone in the Channels pane. No more duplicate subscription in the Code pane, and the component stays self-contained where it has no parent to lean on.
Mirror the self-contained nav destinations (Home, Skills, MCP servers,
Command Center) under /website so clicking them from the channels
sidebar renders inside the channels chrome instead of switching back to
Code. Each mirror route renders the same shared view component, so pages
stay single-source — only the route entry is duplicated.
- Add /website/{home,skills,mcp-servers,command-center} routes rendering
the shared HomeView/SkillsView/McpServersView/CommandCenterView.
- Add navigateToWebsite* bridge functions; SidebarNavSection picks the
/website variant when it renders inside the channels space.
- useAppView maps the /website mirrors to the same view types so sidebar
active-state highlighting works identically in either space.
- WebsiteLayout surfaces the shared header-store title in its top bar for
these channel-less pages, since the channels layout has no HeaderRow.
Inbox and Agents are deep, route-coupled subsystems; they intentionally
still jump to the /code routes for now.
Generated-By: PostHog Code
Task-Id: 93824635-f0b3-4b9d-bf15-f31a4e505939
Render the same ProjectSwitcher (name, email, org, current project) at the bottom of the channels sidebar, using the identical wrapper as the code sidebar's SidebarContent. The component is self-contained (no props), so it sources its own auth/project data in either space. Generated-By: PostHog Code Task-Id: 93824635-f0b3-4b9d-bf15-f31a4e505939
The New task button in the channels sidebar now opens the new-task screen at /website/new instead of jumping to /code, keeping the channels chrome. The route renders the same shared TaskInput (with the same prefill) as the /code/ index, so the page stays single-source. openTaskInput gains a `space` option; SidebarNavSection passes space: "website" when it renders inside the channels space. Generated-By: PostHog Code Task-Id: 93824635-f0b3-4b9d-bf15-f31a4e505939
A channel's name is used verbatim as its server-side filesystem path segment, so constrain it to [a-z0-9-] only — no spaces, uppercase, or other characters. Add a shared validateChannelName helper in core (with tests) and wire it into the Create and Rename channel modals: an inline red error shows under the field and the submit button is disabled until the name is valid, matching the existing GitBranchDialog validation pattern. Generated-By: PostHog Code Task-Id: 8af89649-aa10-4e81-baf6-2c60f2116012
SidebarNavSection derived the Command Center badge count from its own useTasks subscription. When composed inside SidebarMenu (the Code pane), SidebarMenu already subscribes to the identical task query, so this added a second live subscription to the same data. Pass the count down from SidebarMenu — which already holds the task map and command-center cells — and gate the nav section's own useTasks with `enabled` so it only fetches when rendered standalone in the Channels pane. The component stays self-contained in that layout while dropping the duplicate subscription in the Code pane. Generated-By: PostHog Code Task-Id: 8af89649-aa10-4e81-baf6-2c60f2116012
Summary
Adds the Code pane's sidebar navigation section — New task, Home, Search, Inbox, Agents, Skills, MCP servers, Command Center — to the Channels pane.
The section is extracted into a single self-contained
SidebarNavSectioncomponent rendered in both sidebars, so pages stay single-source. For the self-contained destinations (Home, Skills, MCP servers, Command Center), parallel/website/*routes render the same shared view components, so clicking them from the channels sidebar stays in the channels chrome instead of switching back to Code.Inbox and Agents are deep, route-coupled subsystems (their
/code/inbox/*and/code/agents/scouts/*route literals are hardcoded across ~15 components and core constants), so they intentionally still jump to the existing/coderoutes for now. Search opens the command-menu overlay in place.Changes
Shared nav section
SidebarNavSection.tsx— holds the full nav block plus every item's data/handlers (active flags fromuseAppView(), inbox signal-count, Command Center count,HOME_TAB_FLAGgating, click handlers). Reuses the existing*Itemcomponents unchanged.SidebarMenu.tsxrenders<SidebarNavSection />(Code pane);__root.tsxrenders it in the channels-space left column aboveChannelsList.Stay-in-channels routing
/website/{home,skills,mcp-servers,command-center}route files, each rendering the sharedHomeView/SkillsView/McpServersView/CommandCenterView.navigationBridge.tsgainsnavigateToWebsite*functions;SidebarNavSectionpicks the/websitevariant when it renders inside the channels space (detected via router pathname).useAppView.tsmaps the/websitemirrors to the same view types so sidebar active-state highlighting works identically in either space.WebsiteLayout.tsxsurfaces the shared header-store title in its top bar for these channel-less pages (the channels layout has noHeaderRow).Behavior
/website/*, with the matching item active and its title in the content top bar.Verification
pnpm build— all packages build (route tree regenerated).pnpm --filter @posthog/ui typecheck— clean.biome checkon changed files (lint + import sorting) — clean.To try it:
pnpm dev, open the Channels space via the AppNav rail (PROJECT_BLUEBIRD_FLAGdefaults on in dev), then click Skills / MCP servers / Command Center / Home — you stay in the channels sidebar.🤖 Generated with Claude Code